/**
 * @file population.h
 * @brief Defines interface for a group of modules used in a genetic algorithm
 * @author Scott Moyers
 * @date 2009
 */

/**
 * @page AddingGeneticFunctions Adding your own genetic functions
 *
 * @section About
 *
 * If you want to try out some new genetic functions there are currently
 * three sections you can add functions to, crossover, selection and mutation.
 *
 * @section Crossover
 *
 * Crossover functions are held in the crossover.h and crossover.c files.
 * They all take a random number generator and 2 parent modules as inputs and
 * return a new module that is a combination of the two.
 *
 * To add a crossover function you need to:\n
 * 1. In crossover.h increment the CROSS_FUNCS define.\n
 * 2. Add your function prototype to the end of the crossover.h file.\n
 * 3. In crossover.c add your function to the crossover_functions array.\n
 * 4. Add your function and function name string to the "get_crossover_func" file.\n
 * 5. Implement your new function at the end of the crossover.c file.\n
 *
 * You should then be able to use your crossover string name in a config file.
 *
 * @section Selection
 *
 * Selection functions are held in the selection.h and selection.c files.
 * They all take a random number generator an array of modules with the array size
 * and the number of modules to select as inputs. At the end of the selection process
 * the modules you have selected should be in the first "sel" places of the input array.
 *
 * To add a selection function you need to:\n
 * 1. In selection.h increment the SEL_FUNCS define.\n
 * 2. Add your function prototype to the end of the selection.h file.\n
 * 3. In selection.c add your function to the selection_functions array.\n
 * 4. Add your function and function name string to the "get_selection_func" file.\n
 * 5. Implement your new function at the end of the selection.c file.\n
 *
 * You should then be able to use your selection string name in a config file.
 *
 * @section Mutation
 *
 * Mutation functions are held in the mutate.h and mutate.c files.
 * They all take a random number generator and a module as input. The input module
 * is mutated by your mutation function. They return a 0 if the mutation was 
 * successful and a 1 otherwise.
 *
 * To add a mutation function you need to:\n
 * 1. In mutate.h increment the MUT_FUNCS define.\n
 * 2. Add your function prototype to the end of the mutate.h file.\n
 * 3. In mutate.c add your function to the mutation_functions array.\n
 * 4. Add your function and function name string to the "get_mutation_func" file.\n
 * 5. Implement your new function at the end of the mutate.c file.\n
 *
 * You should then be able to use your mutation string name in a config file.
 *
 */

#ifndef MY_POPULATION_H
#define MY_POPULATION_H

#include "module.h"
#include "mutate.h"
#include "crossover.h"
#include "selection.h"
#include "random_module_generator.h"
#include "truth_table.h"

/**
 * @brief holds data for a GA population of modules
 */
struct Population {
	struct Module **modules;        /**< array of pointers to modules making up the population */
	unsigned int size;              /**< number of modules in the population */
	struct TruthTable *truth_table; /**< truth table we're aiming to generate */
	unsigned int max_ckt_size;      /**< max size of circuits to generate (circuits can still grow during mutation!) */

	SelectionFunc *selection_funcs;  /**< allowed selections */
	unsigned int n_selection_funcs;  /**< number of selection functions */

	MutationFunc *mutation_funcs;    /**< allowed mutations */
	unsigned int n_mutation_funcs;   /**< number of mutation functions */
	unsigned int max_mutations;      /**< maximum mutations to perform on one module */

	CrossoverFunc *crossover_funcs;  /**< allowed crossovers */
	unsigned int n_crossover_funcs;  /**< number of crossover functions */
	float crossover_prob;            /**< probability of a crossover taking place when making a new child */

	unsigned int generation;         /**< current generation */
	unsigned int max_generations;    /**< number of generations to run for */

	/* the sum of the keep and breed ratio must be <= 1.0 */
	float keep_ratio;                /**< ratio to select for the next generation */
	float breed_ratio;               /**< ratio to breed from the selection for next generation */
};

/**
 * creates a new population of the given size
 * @return a pointer to a new Population
 */
struct Population *new_population();

/**
 * initialises a population to default values
 * @param p pointer to the population to initialise
 */
void init_population(struct Population *p);

/**
 * deletes a population
 * @param p the population
 */
void delete_population(struct Population *p);

/**
 * removes and deletes all modules in the population
 * @param p the population
 */
void kill_population(struct Population *p);

/**
 * adds n random modules to the population
 * @param p the population
 * @param dsfmt pointer to a random number generator
 * @param n the number of random modules to add
 */
void generate_population(struct Population *p, dsfmt_t *dsfmt, unsigned int n);

/**
 * runs the fitness function on the entire population
 * to decide how good each module is
 * @param p the population to evaluate
 */
void evaluate_population(struct Population *p);

/**
 * moves onto the next generation by performing
 * selection, crossover (maybe) and mutation
 * @note initial population should have it's fitness
 * evaluated before calling this
 * @param p the population
 * @param dsfmt a random number generator
 */
void next_generation(struct Population *p, dsfmt_t *dsfmt);

/**
 * adds an allowed selection function to the population
 * @param p the population
 * @param f pointer to a random selection Function
 */
void add_selection_func(struct Population *p, SelectionFunc f);

/**
 * adds an array of selection functions to the population
 * @param p the population
 * @param f pointer to an array of selection functions
 * @param n the number of functions in the array
 */
void add_selection_funcs(struct Population *p, SelectionFunc f[], unsigned int n);

/**
 * adds an allowed mutation function to the population
 * @param p the population
 * @param f pointer to a random mutation Function
 */
void add_mutation_func(struct Population *p, MutationFunc f);

/**
 * adds an array of mutation functions to the population
 * @param p the population
 * @param f pointer to an array of mutation functions
 * @param n the number of functions in the array
 */
void add_mutation_funcs(struct Population *p, MutationFunc f[], unsigned int n);

/**
 * adds an allowed crossover function to the population
 * @param p the population
 * @param f pointer to a random crossover Function
 */
void add_crossover_func(struct Population *p, CrossoverFunc f);

/**
 * adds an array of crossover functions to the population
 * @param p the population
 * @param f pointer to an array of crossover functions
 * @param n the number of functions in the array
 */
void add_crossover_funcs(struct Population *p, CrossoverFunc f[], unsigned int n);

/**
 * returns a pointer to the fittest module in the given population
 * @param p the population
 * @return a pointer to the fittest module
 */
struct Module *get_fittest(struct Population *p);

/**
 * returns the position in the population that the fittest module is
 * @param p the population
 * @return position in the population of the fittest module
 */
unsigned int get_fittest_pos(struct Population *p);

/**
 * returns the average (mean) fitness of the population
 * @param p the population
 * @return the mean fitness of the population
 */
double average_fitness(struct Population *p);

/**
 * returns the sum of the fitnesses of the population
 * @param p the population
 * @return the sum of the populations fitnesses
 */
double sum_fitness(struct Population *p);

#endif
